/*    
 * Copyright (C) 2006  ankostis, Stefan Kuper

 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package net.googlecode.weblogic_jaxb1codec;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

import weblogic.xml.schema.binding.SerializationContext;
import weblogic.xml.stream.ElementFactory;
import weblogic.xml.stream.XMLOutputStream;
import weblogic.xml.stream.XMLStreamException;
import weblogic.xml.stream.events.EndPrefixMappingEvent;
import weblogic.xml.stream.events.StartPrefixMappingEvent;

/**
 * @author SK, ankostis
 * 
 * IMPL HELP:  The default prefixes are, by default, empty strings.
 * I'm not sure when doc has noNamespaceSchema.
 */
class SerializerContentHandler implements ContentHandler {

//    private SerializationContext context;

    private String outEncoding;

    private XMLOutputStream outputStream;
    
    private NamespaceStack xmlnsStack;

    private boolean debug;
    private int debugIdentationLevel;

    
    public SerializerContentHandler(XMLOutputStream outputStream, SerializationContext context, String outEncoding, 
            boolean debug) {
        this.outputStream = outputStream;
//        this.context = context;
        this.outEncoding = outEncoding;
        this.debug = debug;
        this.xmlnsStack = new NamespaceStack(debug);
    }
    
    private void printIndented(String s) {
        for (int i = 0; i < debugIdentationLevel; i++)
            s = "  " + s;
        System.out.print("\n[SIM]" + s); // SIM = SIMULATED
    }
    
    /**
     * @throws SAXException 
     * @inheritDoc
     */
    public void startPrefixMapping(final String prefix, final String uri) throws SAXException {
        xmlnsStack.pushMapping(prefix, uri);
        try {
            StartPrefixMappingEvent ev = new StartPrefixMappingEvent(prefix, uri);
//            System.out.print("\n[PREFIX_START]:"+ev.getPrefix() + ", URI:"+ev.getNamespaceUri());
            outputStream.add(ev);
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while starting prefix mapping '" + prefix + "': " + uri, ex);
        }
    }

    /**
     * @throws SAXException 
     * @inheritDoc
     */
    public void endPrefixMapping(final String prefix) throws SAXException {
        xmlnsStack.popMapping(prefix);
        try {
            outputStream.add(new EndPrefixMappingEvent(prefix));
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while ending prefix mapping '" + prefix, ex);
        }
    }

	/**
	 * @inheritDoc
	 */
	public void startElement(String uri, String localName, String qName, Attributes saxAttrs) throws SAXException {
		try {
//            /* IMPL HELP:
//             * ---------
//             * The elFact.createElement(uri, local, prefix) generates a namespace attr decl,
//             * the elFact.createElement(uri, local) does not.
//             * 
//             * The JAXB generates empty-string prefixes for the default namespace.
//             * If these empty prefixes are fed to elFact.createElement(), 
//             * the wlStax generates a namespace decl on each element on the default namespace, and also
//             * all elements start with ':'.
//             */
//            String prefix = xmlnsStack.findPrefix(uri);
//            if (prefix == null || prefix.length() == 0)
//                outputStream.add(ElementFactory.createStartElement(uri, localName));
//            else
//                outputStream.add(ElementFactory.createStartElement(uri, localName, preffix));
                outputStream.add(ElementFactory.createStartElement(qName));

            if (debug) {
                printIndented("<"+qName);
//            printIndented("<{"+uri+"}"+qName);
//            printIndented("<"+findPrefix(uri)+":"+localName +"("+qName+")");
                debugIdentationLevel++;
            }
            
            // Add any namespaces.
            xmlnsStack.addNewNamespaceDeclAttrs(outputStream);
            
            // Add any atributes.
            for(int i = 0; i < saxAttrs.getLength(); i++) {
                outputStream.add(ElementFactory.createAttribute(
                        saxAttrs.getURI(i), saxAttrs.getQName(i), saxAttrs.getValue(i)));
                if (debug)
                    System.out.print(" " + saxAttrs.getQName(i)+"='"+saxAttrs.getValue(i)+"'");
            }
            if (debug)
                System.out.print(">");
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while starting element '" + localName + "': " + ex.getMessage(), ex);
		}
	}

	/**
	 * @inheritDoc
	 */
	public void endElement(String uri, String localName, String qName) throws SAXException {
		try {
			outputStream.add(ElementFactory.createEndElement(uri, localName, xmlnsStack.findPrefix(uri)));
            if (debug) {
                debugIdentationLevel--;
                printIndented("</"+qName+">");
            }
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while ending element '" + localName + "': " + ex.getMessage(), ex);
        }
	}

	/**
	 * @inheritDoc
	 */
	public void characters(char[] ch, int start, int length) throws SAXException {
		try {
			outputStream.add(ElementFactory.createCharacterData(String.copyValueOf(ch, start, length)));
            if (debug)
                System.out.print(String.copyValueOf(ch, start, length));
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while adding characters: " + ex.getMessage(), ex);
		}
	}

    /**
     * @throws SAXException 
     * @inheritDoc
     */
    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException{
        try {
            outputStream.add(ElementFactory.createSpace(String.copyValueOf(ch, start, length)));
            if (debug)
                System.out.print(String.copyValueOf(ch, start, length));
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while adding ignoreable whitespace: " + ex.getMessage(), ex);
        }
    }

    /**
     * @throws SAXException 
     * @inheritDoc
     */
    public void processingInstruction(String target, String data) throws SAXException{
        //XXX: Untested code.
        try {
            outputStream.add(
                    ElementFactory.createProcessingInstruction(ElementFactory.createXMLName(target), 
                            data));
            if (debug)
                System.out.print(target+"='"+data+"'");
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while creating proc instruction: " + ex.getMessage(), ex);
        }
    }

    /**
     * @inheritDoc
     */
    public void setDocumentLocator(Locator locator) {
        if (debug)
            System.out.println("Document LOCATOR:"+locator);
    }

    /**
     * @inheritDoc
     */
    public void skippedEntity(String name){
         System.out.println("SKIPED ENTITY: " +name);
    }

    /**
     * @throws SAXException 
     * @inheritDoc
     */
    public void startDocument() throws SAXException{
        try {
            outputStream.add(ElementFactory.createStartDocument(outEncoding));
            if (debug)
                System.out.print("[SIM]<? encoding=\"" + outEncoding + "\" ?>");
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while creating doc header: " + ex.getMessage(), ex);
        }
    }

    /**
     * @throws XMLStreamException 
     * @inheritDoc
     */
    public void endDocument() throws SAXException{
        try {
            outputStream.flush();
            if (debug)
                System.out.print("[SIM]<? encoding=\"" + outEncoding + "\" ?>");
        } catch (XMLStreamException ex) {
            throw new SAXException("Error while flushing completed document: " + ex.getMessage(), ex);
        }
    }
}
